package org.ala.tiktools.rpc.kcp.protocol.receiver.queue;

import java.util.List;

import org.ala.tiktools.rpc.kcp.protocol.segment.KcpSegment;
import org.ala.tiktools.tools.list.DoubleLinkedList;

import io.netty.buffer.ByteBuf;


/**
 * @author ala
 * @date 2025-04-13 11:10
 */
public class KcpReceiveLinkedList extends DoubleLinkedList<KcpSegment> {


    /**
     *  是否超过 rmtWnd
     */
    public boolean isWarningLine(int rmtWnd) {
        return size() >= rmtWnd;
    }


    /**
     *  rcvQue 中一个完整消息的字节数
     *  @return -1:queue为空 | -2:queue中还么有收到一个完整消息 | >0 一个完整消息的字节数
     */
    public int peekSize() {
        if (this.isEmpty()) {
            return -1;
        }
        //  同一个消息，在rcvQue中按frg倒排。所以第一个消息的frg就是一个消息的结尾。frg从0开始，frg+1就是一个完整消息的分包数
        //  如果 frg+1 > size，说明还没有接收到一个完整数据包
        Node<KcpSegment> crt = HEAD.next;
        if (crt.val.getFrg()+1 > size()) {
            return -2;
        }

        int len = 0;
        while (crt != TAIL) {
            len += crt.val.getData().readableBytes();
            if (crt.val.getFrg() == 0) {
                break;
            }
            crt = crt.next;
        }
        return len;
    }


    /**
     *  rcvQue -> ByteBuf，直到frg == 0为止
     *  @return  读取的字节数
     */
    public int writeToByteBufAndRemove(ByteBuf buf) {
        int len = 0;

        for (KcpSegment seg = peek() ; seg != null ; seg = peek()) {
            len += seg.getData().readableBytes();
            buf.writeBytes(seg.getData());

            int frg = seg.getFrg();
            seg.recycle(true);
            pop();

            if (frg == 0) { break; }
        }
        return len;
    }
    /**
     *  rcvQue[i].data -> bufs[i]，直到frg == 0为止
     *  @return  读取的字节数
     */
    public int writeToByteBufsAndRemove(List<ByteBuf> bufs) {
        int len = 0;
        for (KcpSegment seg = peek() ; seg != null ; seg = peek()) {
            len += seg.getData().readableBytes();
            bufs.add(seg.getData());

            int frg = seg.getFrg();
            //	data的引用计数不会累减，需要外面自行控制
            seg.recycle(false);
            pop();

            if (frg == 0) { break; }
        }
        return len;
    }


    /**
     *  将受到的数据按sn号升序放入sndBuf列表
     *  <p> sn号大概率收到的是排在末尾的
     */
    public void parseData(KcpSegment seg) {
        long sn = seg.getSn();
        //  找第一个 < sn 的节点，seg就插入到他的后面
        Node<KcpSegment> crt = TAIL.prev;
        boolean reRecv = false;
        while (crt != HEAD) {
            if (crt.val.getSn() == sn) {
                reRecv = true;
                break;
            } else if (crt.val.getSn() < sn) {
                break;
            }
            crt = crt.prev;
        }
        //  如果重复收到了相同的sn号，释放掉本次收到的消息
        if (reRecv) {
            seg.recycle(true);
        } else {
            insertAfter(crt, seg);
        }
    }
}
